﻿using System.Reflection;

namespace Amber.Arch.Catty
{
    public static class CatExtensions
    {
        public static Cat Register(this Cat cat, Assembly assembly)
        {
            var typedAttributes = from type in assembly.GetExportedTypes()
                                  let attribute = type.GetCustomAttribute<MapToAttribute>()
                                  where attribute != null
                                  select new
                                  {
                                      ServiceType = type,
                                      Attribute = attribute
                                  };

            foreach (var typedAttribute in typedAttributes)
            {
                cat.Register(typedAttribute.Attribute.ServiceType, typedAttribute.ServiceType,
                             typedAttribute.Attribute.LifeTime);
            }
            return cat;
        }

        public static Cat Register(this Cat cat, Type from, Type to, LifeTime lifeTime)
        {
            object? factory(Cat c, Type[] args) => Create(c, to, args);
            cat.Register(new ServiceRegistry(from, lifeTime, factory));
            return cat;
        }

        public static Cat Register<TFrom, TTo>(this Cat cat, LifeTime lifeTime) where TTo : TFrom
            => cat.Register(typeof(TFrom), typeof(TTo), lifeTime);

        public static Cat Register(this Cat cat, Type serviceType, object instance)
        {
            Func<Cat, Type[], object?> factory = (_, arguments) => instance;
            cat.Register(new ServiceRegistry(serviceType, LifeTime.Root, factory));
            return cat;
        }

        public static Cat Register<TService>(this Cat cat, TService instance) where TService : class
        {
            Func<Cat, Type[], object?> factory = (_, arguments) => instance;
            cat.Register(new ServiceRegistry(typeof(TService), LifeTime.Root, factory));
            return cat;
        }

        public static Cat Register(this Cat cat, Type serviceType, Func<Cat, object> factory, LifeTime lifeTime)
        {
            cat.Register(new ServiceRegistry(serviceType, lifeTime, (c, arguments) => factory(c)));
            return cat;
        }

        public static Cat Register<TService>(this Cat cat, Func<Cat, TService> factory, LifeTime lifeTime) where TService : class
        {
            cat.Register(new ServiceRegistry(typeof(TService), lifeTime, (c, arguments) => factory(c)));
            return cat;
        }

        private static object? Create(Cat cat, Type type, Type[] genericArguments)
        {
            if (genericArguments.Length > 0)
            {
                type = type.MakeGenericType(genericArguments);
            }
            var constructors = type.GetConstructors();
            if (constructors.Length == 0)
            {
                throw new InvalidOperationException($"没有构造函数无法创建{type}类型的实例");
            }
            var constructor = constructors.FirstOrDefault(x => x.GetCustomAttributes(false).OfType<InjectionAttribute>().Any());
            constructor ??= constructors.Last();
            var parameters = constructor.GetParameters();
            if (parameters.Length == 0)
            {
                return Activator.CreateInstance(type);
            }
            var arguments = new object?[parameters.Length];
            for (int i = 0; i < arguments.Length; i++)
            {
                arguments[i] = cat.GetService(parameters[i].ParameterType);
            }
            return constructor.Invoke(arguments);
        }

        public static Cat CreateChild(this Cat cat) => new Cat(cat);
    }
}
